package lyd;


import robocode.HitByBulletEvent;
import robocode.HitRobotEvent;
import robocode.Robot;
import robocode.ScannedRobotEvent;

/**
 * Moves around the outer edge with the gun facing in. At every corner, it spins 3 full
 * circles while firing at an enemy. When hit by a bullet, robot will move to center and
 * spin 3 times while shooting. Movement behavior alternates after being hit.
 * 
 * Derived from Walls sample robot.
 * 
 * @author Daniel Ly
 * @version September 26 2011
 */
public class CompetitiveRobot extends Robot {

  boolean look; // Don't turn if there's a robot there
  double moveAmount; // How much to move
  int behavior = 0;

  /**
   * Move along the walls.
   */
  public void run() {
    // Initialize moveAmount to the maximum possible for this battlefield.
    moveAmount = Math.max(getBattleFieldWidth(), getBattleFieldHeight());
    
    while (true) {
      if (behavior == 0) {
        wallMove();
      }
      else if (behavior == 1) {
        centerMove();
      }
    }
  }
  
  /**
   * Moves to a wall and continues moving along walls with stops at each corner.
   */
  public void wallMove () {
    look = false;
    turnLeft(getHeading() % 90);
    ahead(moveAmount);
    // Turn the gun to turn right 90 degrees to face inwards.
    look = true;
    turnGunRight(90);
    turnRight(90);

    while (behavior == 0) {
      look = true;
      ahead(moveAmount);
      look = false;
      turnRight(90);
      // Spin in three full circles while firing at an enemy
      turnGunRight(1080);
    }
  }
  
  /**
   * Moves to the center and spins/shoots until hit.
   */
  public void centerMove () {
    goToCenter();
    while (behavior == 1) {
      turnGunRight(1080);
    }
  }
  
  /**
   * Determines the initial location of the robot. Locations include quadrants 1 - 4,
   * along the center axis, or already in the center. Knowing the location, the robot
   * can then be moved accordingly.
   */
  public void goToCenter () {
    // get coordinates for the middle and for the robot
    int midX = (int) getBattleFieldWidth() / 2;
    int midY = (int) getBattleFieldHeight() / 2;
    int botX = (int) getX();
    int botY = (int) getY();
    
    // find out where the robot is, either along the center x and y axis or in quadrants 1 through 4
    int location = 0;
    double distance = 0;
    if (botX == midX && botY == midY) {
      // robot is in center
      return;
    }
    else if (botX > midX && botY > midY) {
      // robot is in first quadrant
      location = 0;
    }
    else if (botX < midX && botY > midY) {
      // robot is in second quadrant
      location = 1;
    }
    else if (botX < midX && botY < midY) {
      // robot is in third quadrant
      location = 2;
    }
    else if (botX > midX && botY < midY) {
      // robot is in fourth quadrant
      location = 3;
    }
    else if (botX == midX && botY > midY) {
      // robot is on the upper y axis
      location = 4;
    }
    else if (botX == midX && botY < midY) {
      // robot is on the lower y axis
      location = 5;
    }
    else if (botX < midX && botY == midY) {
      // robot is on the left x axis
      location = 6;
    }
    else if (botX > midX && botY == midY) {
      // robot is on the right x axis
      location = 7;
    }
    
    // turn and move towards the center based on location on battle field
    switch (location) {
    case 0:
      // point robot directly downwards
      turnLeft(getHeading() + 180);
      // use arctan to find the angle needed to turn towards center
      turnRight(Math.toDegrees(Math.atan(Math.abs((double) botX - midX) / Math.abs((double) botY -
          midY))));
      // use pythagorean's theorem to get distance to center
      distance = Math.sqrt(Math.pow(((double) botX - midX), 2) + Math.pow(((double) botY - midY),
          2));
      ahead(distance);
      break;
    case 1:
      // point robot directly downwards
      turnLeft(getHeading() + 180);
      // use arctan to find the angle needed to turn towards center
      turnLeft(Math.toDegrees(Math.atan(Math.abs((double) midX - botX) / Math.abs((double) botY -
          midY)))); 
      // use pythagorean's theorem to get distance to center
      distance = Math.sqrt(Math.pow(((double) midX - botX), 2) + Math.pow(((double) botY - midY),
          2));
      ahead(distance);
      break;
    case 2:
      // point robot directly upwards
      turnLeft(getHeading());
      // use arctan to find the angle needed to turn towards center
      turnRight(Math.toDegrees(Math.atan(Math.abs((double) midX - botX) / Math.abs((double) midY -
          botY)))); 
      // use pythagorean's theorem to get distance to center
      distance = Math.sqrt(Math.pow(((double) midX - botX), 2) + Math.pow(((double) midY - botY),
          2));
      ahead(distance);
      break;
    case 3:
      // point robot directly upwards
      turnLeft(getHeading());
      // use arctan to find the angle needed to turn towards center
      turnLeft(Math.toDegrees(Math.atan(Math.abs((double) botX - midX) / Math.abs((double) midY - 
          botY)))); 
      // use pythagorean's theorem to get distance to center
      distance = Math.sqrt(Math.pow(((double) botX - midX), 2) + Math.pow(((double) midY - botY),
          2));
      ahead(distance);
      break;
    case 4:
      // point robot directly downwards
      turnLeft(getHeading() + 180);
      distance = Math.abs((double) botY - midY);
      ahead(distance);
      break;
    case 5:
      // point robot directly upwards
      turnLeft(getHeading());
      distance = Math.abs((double) midY - botY);
      ahead(distance);
      break;
    case 6:
      // point robot directly right
      turnLeft(getHeading() - 90);
      distance = Math.abs((double) midX - botX);
      ahead(distance);
      break;
    case 7:
      // point robot directly left
      turnLeft(getHeading() + 90);
      distance = Math.abs((double) botX - midX);
      ahead(distance);
      break;
    default:
      break;
    }
  }

  /**
   * Move away from enemy.
   * 
   * @param e contains event when running into enemy
   */
  public void onHitRobot(HitRobotEvent e) {
    if (e.getBearing() > -90 && e.getBearing() < 90) {
      back(100);
    }
    else {
      ahead(100);
    }
  }

  /**
   * Fire at enemy.
   * 
   * @param e contains event when an enemy is scanned
   */
  public void onScannedRobot(ScannedRobotEvent e) {
    fire(3);
    // Scan for enemies along the wall.
    if (look) {
      scan();
    }
  }
  
  /**
   * Robot hit! Switch up movement behavior.
   * 
   * @param e contains event when robot is hit
   */
  public void onHitByBullet(HitByBulletEvent e) {
    if (behavior == 0) {
      behavior = 1;
    }
    else {
      behavior = 0;
    }
  }
}
